/*
  ----- Programme de pilotage du programmateur de 93x6 ou 59Cxx -----
	(C) Pierre COL, F4EGQ, avril 1997 / janvier 2004

    					                         Sens Nom     Port//  Centronix
    Support tulipe	                             info broche   DB25  36 broches
      8 broches :
                        1   : CS  (chip select)   <-  D2         4         4
     +---+__+---+       2   : CLK (serial clock)  <-  D1         3         3
 1 --|CS     +5V|-- 8   3   : DI  (data input)    <-  D3         5         5
 2 --|SK    Busy|-- 7   4   : DO  (data output)   ->  Error     15        32
 3 >-|DI     ORG|-- 6   5   : GND (Ground, masse) --  GND  19 à 25   19 à 30
 4 <-|DO     GND|-- 5   6   : ORG (8bits/16bits)  <-  D4         6         6
     +----------+       7   : RDY/Busy (59Cxx)    ->  Ack       10        10
                        8   : VCC (+5V)           <-  D5         7         7
     9306   59c11
     9346   59C22
     9356   59C13
     9366
     9376
     9386
*/

//---------------------------------------------------------------------------

#include <vcl.h>
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include <registry.hpp>
#include "mwprogu1.h"
#include "mwprogu2.h"
#include "mwprogu3.h"

//---------------------------------------------------------------------------

#pragma hdrstop
#pragma package(smart_init)
#pragma link "cspin"
#pragma resource "*.dfm"

//---------------------------------------------------------------------------

// Adresses des ports parallèles :
#define LPT1 0x378
#define LPT2 0x278
#define LPT3 0x3BC

// Types de mémoires :
#define T9306 0
#define T9346 1
#define T9356 2
#define T9366 3
#define T9376 4
#define T9386 5
#define T5911 6
#define T5922 7
#define T5913 8

// Commandes :
#define EWEN   3
#define EWDS   0
#define ERAL   2
#define ERASE 12


//---------------------------------------------------------------------------

int                 TMtaille,TMtype,TMnba,TMnbd;
int                 Tempo,buffer[2049],pointeur;
unsigned char       OUT_LPT;
unsigned short      PortPRN;
bool                Mode16bits,sauverConfig;

//---------------------------------------------------------------------------

TForm1 *Form1;

__fastcall TForm1::TForm1(TComponent* Owner)
    : TForm(Owner)
{
}

//---------------------  Prototypes des routines : --------------------------

unsigned char  inport(unsigned short int);
unsigned char  inportb(unsigned short int);
void           outport(unsigned short int, unsigned char);
void           outportb(unsigned short int, unsigned char);
bool           InitPort(unsigned short int);
bool           Clear_CS(void);
bool           Set_CS(void);
bool           Clear_CLK(void);
bool           Set_CLK(void);
bool           Crenaux_CLK(void);
bool           Clear_DI(void);
bool           Set_DI(void);
bool           Fixer_DI(int);
bool           Clear_ORG(void);
bool           Set_ORG(void);
bool           Alim_OFF(void);
bool           Alim_ON(void);
bool           Teste_Alim_ON(void);
int            Lire_DO(void);
int            Lire_RDY_BUSY(void);
void           AfficheBuffer(void);
bool           MessageErreur(int);
int            LireAdresse(int, int, int);
int            LireTout(int, int, int);
int            Comparer(int, int, int);
int            Commande(int, int, int, bool);
int            EcrireAdresse(int, int, int, int);
int            EcrireTout(int, int, int *, int);
int            RemplirAvec(int, int, int);
void           ParametresMemoire(int, bool);

//---------------------------------------------------------------------------

// D'après le livre de Gérard LEBLANC "Borland C++ Builder 3"
// aux editions "Eyrolles" :
// Lecture du port d'entrée/sortie dont on passe l'adresse ;
// la valeur est renvoyée sous forme d'un octet.
unsigned char inport(unsigned short int Port_)
{
  unsigned char Val_;

  __emit__(0x8B,0x95,&Port_);
  __emit__(0x66,0xEC);
  __emit__(0x88,0x85,&Val_);
  return Val_;
}

// Variante incluant une temporisation :
unsigned char inportb(unsigned short int Port)
{
  unsigned char val;
  for (int n=Tempo;n>=0;n--) val=inport(Port);
  return val;
}

//---------------------------------------------------------------------------

// D'après le livre de Gérard LEBLANC "Borland C++ Builder 3"
// aux editions "Eyrolles" :
// Ecriture d'un port d'entrée/sortie : on passe l'adresse
// 16 bits ("Port") puis la valeur 8 bits ("Val").
void outport(unsigned short int Port_, unsigned char Val_)
{
  __emit__(0x8B,0x95,&Port_);
  __emit__(0x8A,0x85,&Val_);
  __emit__(0x66,0xEE);
}

// Variante incluant une temporisation :
void outportb(unsigned short int Port, unsigned char Val)
{
  for (int n=Tempo;n>=0;n--) outport(Port,Val);
}

//---------------------------------------------------------------------------

// Choix du port parallele (par defaut, LPT1) :
// 'Port' : LPT1 (port 0x378), LPT2 (port 0x278), LPT3 (port 0x3BC)
// Renvoie "false" si le port est absent. Renvoie true si le port
// est présent, PortPRN contient alors son adresse et OUT_LPT est mis à 0x00 :
// B4,B5,B6,B7 = Alim / B1 = CLK / B2 = CS / B3 = DI / Error = entrée DO.
bool InitPort(unsigned short int Port)
{
  Form1->STalim->Caption=" ";
  Form1->STalim->Font->Color=clAqua;
  Form1->STalim->Caption="Alim OFF";
  PortPRN=0;
  switch (Port)
  {
    case LPT1 : PortPRN=LPT1; break;
    case LPT2 : PortPRN=LPT2; break;
    case LPT3 : PortPRN=LPT3; break;
  }
  if (PortPRN!=0)
  {
    bool test1,test2;
    outportb(PortPRN,0xFF); test1=(inportb(PortPRN)==0xFF);
    outportb(PortPRN,0x00); test2=(inportb(PortPRN)==0x00);
    if (test1 && test2)
    {
      OUT_LPT=0x00;
      outportb((unsigned short int) (PortPRN+2), (unsigned char)
               (inportb((unsigned short int) (PortPRN+2)) & 0x000000DF));
    } else PortPRN=0;
  }
  return (PortPRN!=0);
}

//---------------------------------------------------------------------------

// Mise à 0 de la sortie CS :
bool Clear_CS(void)
{
  if (PortPRN!=0)
  {
    OUT_LPT&=0xFB;             // Mise à 0 du bit B2 de OUT_LPT,
    outportb(PortPRN,OUT_LPT); // et mise à jour sur le port de sortie.
    return true;
  } else return false;
}

//---------------------------------------------------------------------------

// Mise à 1 de la sortie CS :
bool Set_CS(void)
{
  if (PortPRN!=0)
  {
    OUT_LPT|=0x04;             // Mise à 1 du bit B2 de OUT_LPT,
    outportb(PortPRN,OUT_LPT); // et mise à jour sur le port de sortie.
    return true;
  } else return false;
}

//---------------------------------------------------------------------------

// Mise à 0 de la sortie CLK :
bool Clear_CLK(void)
{
  if (PortPRN!=0)
  {
    OUT_LPT&=0xFD;             // Mise à 0 du bit B1 de OUT_LPT,
    outportb(PortPRN,OUT_LPT); // et mise à jour sur le port de sortie.
    return true;
  } else return false;
}

//---------------------------------------------------------------------------

// Mise à 1 de la sortie CLK  :
bool Set_CLK(void)
{
  if (PortPRN!=0)
  {
    OUT_LPT|=0x02;             // Mise à 1 du bit B1 de OUT_LPT,
    outportb(PortPRN,OUT_LPT); // et mise à jour sur le port de sortie.
    return true;
  } else return false;
}

//---------------------------------------------------------------------------

// Mise à 0, puis à 1, puis à 0 de la sortie CLK  :
bool Crenaux_CLK(void)
{
  if (PortPRN!=0)
  {
    OUT_LPT&=0xFD;             // Mise à 0 du bit B1 de OUT_LPT,
    outportb(PortPRN,OUT_LPT); // et mise à jour sur le port de sortie.
    OUT_LPT|=0x02;             // Mise à 1 du bit B1 de OUT_LPT,
    outportb(PortPRN,OUT_LPT); // et mise à jour sur le port de sortie.
    OUT_LPT&=0xFD;             // Mise à 0 du bit B1 de OUT_LPT,
    outportb(PortPRN,OUT_LPT); // et mise à jour sur le port de sortie.
    return true;
  } else return false;
}

//---------------------------------------------------------------------------

// Mise à 0 de la sortie DI :
bool Clear_DI(void)
{
  if (PortPRN!=0)
  {
    OUT_LPT&=0xF7;             // Mise à 0 du bit B3 de OUT_LPT,
    outportb(PortPRN,OUT_LPT); // et mise à jour sur le port de sortie.
    return true;
  } else return false;
}

//---------------------------------------------------------------------------

// Mise à 1 de la sortie DI  :
bool Set_DI(void)
{
  if (PortPRN!=0)
  {
    OUT_LPT|=0x08;             // Mise à 1 du bit B3 de OUT_LPT,
    outportb(PortPRN,OUT_LPT); // et mise à jour sur le port de sortie.
    return true;
  } else return false;
}

//---------------------------------------------------------------------------

// Mise à 0 ou 1 de la sortie DI selon la valeur
// du bit "Bit" un entier nul (= 0) ou non (= 1) :
bool Fixer_DI(int Bit)
{
  if (PortPRN!=0)
  {
    if (Bit==0) OUT_LPT&=0xF7; // Mise à 0 du bit B3 de OUT_LPT,
           else OUT_LPT|=0x08; // Mise à 1 du bit B3 de OUT_LPT,
    outportb(PortPRN,OUT_LPT); // et mise à jour sur le port de sortie.
    return true;
  } else return false;
}

//---------------------------------------------------------------------------

// Mise à 0 de la sortie ORG :
bool Clear_ORG(void)
{
  if (PortPRN!=0)
  {
    OUT_LPT&=0xEF;             // Mise à 0 du bit B4 de OUT_LPT,
    outportb(PortPRN,OUT_LPT); // et mise à jour sur le port de sortie.
    return true;
  } else return false;
}

//---------------------------------------------------------------------------

// Mise à 1 de la sortie ORG  :
bool Set_ORG(void)
{
  if (PortPRN!=0)
  {
    OUT_LPT|=0x10;             // Mise à 1 du bit B4 de OUT_LPT,
    outportb(PortPRN,OUT_LPT); // et mise à jour sur le port de sortie.
    return true;
  } else return false;
}

//---------------------------------------------------------------------------

// Arrêt de l'alimentation de la mémoire :
bool Alim_OFF(void)
{
  if (PortPRN!=0)
  {
    Form1->STalim->Caption=" ";
    Form1->STalim->Font->Color=clAqua;
    Form1->STalim->Caption="Alim OFF";
    OUT_LPT=0x00;              // Mise à 0 des 8 bits OUT_LPT,
    outportb(PortPRN,OUT_LPT); // et mise à jour sur le port de sortie.
    return true;
  } else return false;
}

//---------------------------------------------------------------------------

// Alimentation de la mémoire :
bool Alim_ON(void)
{
  if (PortPRN!=0)
  {
    Form1->STalim->Caption=" ";
    Form1->STalim->Font->Color=clWhite;
    Form1->STalim->Caption="Alim ON";
                                // Mise à 1 des bits B5, B6 et B7 de OUT_LPT
    OUT_LPT|=0xF0;              // ainsi que de la sortie ORG (mode 16 bits),
    outportb(PortPRN,OUT_LPT);  // et mise à jour sur le port de sortie.
    Sleep(70);                  // Petite tempo...
    return true;
  } else return false;
}

//---------------------------------------------------------------------------

// Renvoie 'true' si l'alimentation est active :
bool Teste_Alim_ON(void)
{
  if (PortPRN!=0) return ((inportb(PortPRN) & 0xE0)==0xE0);
             else return false;
}

//---------------------------------------------------------------------------

// Renvoie l'etat de la sortie DO de la memoire ('0' ou '1').
int Lire_DO(void)
{
  return (int) ((inportb((unsigned short int) (PortPRN+1)) & 0x08)>>3);
}

//---------------------------------------------------------------------------

// Renvoie l'etat de la sortie RDY/BUSY (pour les 59C11 / 22 / 13 uniquement).
int Lire_RDY_BUSY(void)
{
  return (int) ((inportb((unsigned short int) (PortPRN+1)) & 0x40)>>6);
}

//---------------------------------------------------------------------------

// Affiche le buffer dans la grille :
void AfficheBuffer(void)
{
  if (Mode16bits)
  {
    for (int k=0;k<1024;k++)
    {
      if (k<TMtaille)
           Form1->SGB->Cells[(k%8)+1][(k/8)+1]=" "+IntToHex(buffer[k],4);
      else Form1->SGB->Cells[(k%8)+1][(k/8)+1]=" ";
    }
  }
  else
  {
    for (int y=0;y<128;y++)
    for (int x=0;x<16;x++)
    {
      int ptr=x+(y<<4);
      if (ptr<TMtaille)
           Form1->SGB->Cells[x+1][y+1]=IntToHex((buffer[ptr] & 0x000000FF),2);
      else Form1->SGB->Cells[x+1][y+1]=" ";
    }
  }
}

//---------------------------------------------------------------------------

// Affiche le message d'erreur spécifié ("numero").
// renvoie false si le "numero" fourni est inconnu.
bool MessageErreur(int numero)
{
  bool existe=true;

  switch(numero)
  {
    case -1 : Form1->Etat->Caption=
                "Erreur : vous devez choisir le port parallèle actif !";
              break;
    case -2 : Form1->Etat->Caption=
                "Erreur : vous devez alimenter la mémoire !";
              break;
    case -3 : Form1->Etat->Caption=
                "Erreur : mémoire absente, défectueuse, "
                "ou type choisi incorrect !";
              break;
    case -4 : Form1->Etat->Caption=
                "Erreur : les données sont verrouillées, ou bien le port // "
                " n'est pas le bon !";
              break;
    case -5 : Form1->Etat->Caption="Erreur : mémoire défectueuse ?"
                " imprimante branchée ?";
              break;
    case -6 : Form1->Etat->Caption="Erreur : l'adresse sélectionnée se trouve"
                " hors de l'espace mémoire !";
              break;
    default : Form1->Etat->Caption="Erreur non répertoriée !";
              existe=false;
  }
  return existe;
}

//---------------------------------------------------------------------------

//  Lit une adresse quelconque de la mémoire.
// 'adresse'         : adresse de la mémoire à lire (0 à 63 pour une 9346).
// 'nb_bits_adresse' : nombre de bits de l'adresse, selon le type de mémoire.
// 'nb_bits_data'    : nombre de bits de la donnée, selon le mode de lecture.
//  - Renvoie la donnée (0 <= donnée <= 65535) s'il n'y a pas eu d'erreur.
//  - Renvoie -1 si le aucun port parallèle n'est sélectionné.
//  - Renvoie -2 si le système n'est pas alimenté.
//  - Renvoie -3 si la mémoire est absente ou défectueuse.
//  - Renvoie -5 si un "0" est lu sur la sortie à l'initialisation.
int LireAdresse(int adresse, int nb_bits_adresse, int nb_bits_data)
{
  if (PortPRN!=0)
  {
    if (Teste_Alim_ON())
    {
      if (nb_bits_data==8) Clear_ORG(); else Set_ORG();
      Clear_CLK();
      Clear_DI();
      Clear_CS();
      Crenaux_CLK();
      if (Lire_DO()==1) // Vérifier que la sortie n'est pas à "0" !
      {
        Set_CS();
        Crenaux_CLK();
        Set_DI();
        Crenaux_CLK();
        Crenaux_CLK();
        Clear_DI();
        Crenaux_CLK();
        for (int posi_bit=(nb_bits_adresse-1);posi_bit>=0;posi_bit--)
        {
          Fixer_DI((adresse >> posi_bit) & 1);
          Crenaux_CLK();
        }
        Clear_DI();
        if (Lire_DO()==0)
        {
          int data=0;

          for (int posi_bit=(nb_bits_data-1);posi_bit>=0;posi_bit--)
          {
            Crenaux_CLK();
            data+=(Lire_DO() << posi_bit);
          }
          Clear_CS();
          return data;
        }
        else
        {
          Clear_CS();
          return -3;
        }
      } else return -5;
    } else return -2;
  } else return -1;
}

//---------------------------------------------------------------------------

//  Lit toute la mémoire et met à jour le 'buffer'.
// 'nb_data'         : nombre d'adresses de la mémoire (ex : 64 pour une 9346).
// 'nb_bits_adresse' : nombre de bits de l'adresse, selon le type de mémoire.
// 'nb_bits_data'    : nombre de bits de la donnée, selon le mode de lecture.
//  - Renvoie '0' s'il n'y a pas eu d'erreur.
//  - S'interrompt et renvoie -1 si le aucun port parallèle n'est sélectionné.
//  - S'interrompt et renvoie -2 si le système n'est pas alimenté.
//  - S'interrompt et renvoie -3 si la mémoire est absente ou défectueuse.
int LireTout(int nb_data, int nb_bits_adresse, int nb_bits_data)
{
  int resultat=0,valeur;

  for (int ptr_adr=0;ptr_adr<nb_data;ptr_adr++)
  {
    valeur=LireAdresse(ptr_adr,nb_bits_adresse,nb_bits_data);
    if (valeur<0) { resultat=valeur; ptr_adr=nb_data; }
             else buffer[ptr_adr]=valeur;
  }
  return resultat;
}

//---------------------------------------------------------------------------

//  compare les contenus de la mémoire et du buffer.
// 'nb_data'         : nombre d'adresses de la mémoire (ex : 64 pour une 9346).
// 'nb_bits_adresse' : nombre de bits de l'adresse, selon le type de mémoire.
// 'nb_bits_data'    : nombre de bits de la donnée, selon le mode de lecture.
//  - Renvoie le nombre de différences (supérieur ou égal à 0).
//  Sauf en cas d'erreur de lecture :
//  - S'interrompt et renvoie -1 si le aucun port parallèle n'est sélectionné.
//  - S'interrompt et renvoie -2 si le système n'est pas alimenté.
//  - S'interrompt et renvoie -3 si la mémoire est absente ou défectueuse.
int Comparer(int nb_data, int nb_bits_adresse, int nb_bits_data)
{
  int resultat=0,valeur,erreur=0;

  for (int ptr_adr=0;ptr_adr<nb_data;ptr_adr++)
  {
    valeur=LireAdresse(ptr_adr,nb_bits_adresse,nb_bits_data);
    if (valeur<0) { resultat=valeur; ptr_adr=nb_data; }
             else { if (buffer[ptr_adr]!=valeur) erreur++; }
  }
  if (resultat>=0) resultat=erreur;
  return resultat;
}

//---------------------------------------------------------------------------

// Commande générale de la mémoire :
// 'fonction' : "EWEN"  = Autorise l'effacement / l'écriture.
//              "EWDS"  = Interdit l'effacement / l'écriture (verrouillage).
//              "ERAL"  = Efface toute la mémoire.
//              "ERASE" = Efface la case pointée par "adresse".
// 'nb_bits_adresse' : nombre de bits d'adresse (6 pour une 9306 ou une 9346).
// 'adresse' : utilisée seulement par la commande ERASE (0 à 63 pour une 9346),
//             pour les autres commandes, valeur indifférente (0 par exemple).
// 'mode_16_bits' : mettre à true en mode 16 bits, et à false en mode 8 bits.
// Renvoie 0 si tout a bien fonctionné, sinon renvoie un nombre négatif qui
// indique la nature de l'erreur (consultez la fonction "MessageErreur").
int Commande(int fonction, int nb_bits_adresse, int adresse, bool mode_16_bits)
{
  int resultat,code,adr;

  if (PortPRN!=0)
  {
    if (Teste_Alim_ON())
    {
      if (fonction!=ERASE) adr=0;
                      else adr=adresse & ((1 << nb_bits_adresse)-1);
      code=(fonction<<(nb_bits_adresse-2)) | adr;
      code|=(1 << (nb_bits_adresse+2));
      if (mode_16_bits) Set_ORG(); else Clear_ORG();
      Clear_CLK();
      Clear_DI();
      Clear_CS();
      Crenaux_CLK();
      if (Lire_DO()==1) // Vérifier que la sortie n'est pas à "0" !
      {
        Set_CS();
        Crenaux_CLK();
        for (int posi_bit=(nb_bits_adresse+2);posi_bit>=0;posi_bit--)
        {
          int bit=(code >> posi_bit) & 1;
          Fixer_DI(bit);
          Crenaux_CLK();
        }
        Clear_DI();
        Clear_CS();
        Crenaux_CLK();
        resultat=0;
        if ((fonction==ERAL)||(fonction==ERASE))
        {
          Set_CS();
          int attente1=0,attente2=0;
          while (((Lire_DO()==1)&&(Lire_RDY_BUSY()==1))&&(attente1<30000))
            attente1++;
          while (((Lire_DO()==0)||(Lire_RDY_BUSY()==0))&&(attente2<30000))
            attente2++;
          if ((attente1==30000)||(attente2==30000)) resultat=-4;
        }
      } else resultat=-5;
    } else resultat=-2;
  } else resultat=-1;
  return resultat;
}

//---------------------------------------------------------------------------

// Ecriture d'une donnée 8 ou 16 bits à une adresse précise :
// 'nb_bits_adresse' : nombre de bits d'adresse (6 pour une 9306 ou une 9346).
// 'adresse' : utilisée seulement par la commande ERASE (0 à 63 pour une 9346),
//             pour les autres commandes, valeur indifférente (0 par exemple).
// 'nb_bits_data' : nombre de bits de donnée (8 ou 16 selon le mode et ORG).
// 'data' : donnée numérique à écrire à l'adresse fournie.
// Renvoie 0 si tout a bien fonctionné, sinon renvoie un nombre négatif qui
// indique la nature de l'erreur (consultez la fonction "MessageErreur").
int EcrireAdresse(int nb_bits_adresse, int adresse, int nb_bits_data, int data)
{
  int resultat,code,adr,attente1,attente2;

  if (PortPRN!=0)
  {
    if (Teste_Alim_ON())
    {
      adr=adresse & ((1 << nb_bits_adresse)-1);
      code=adr | (5 << nb_bits_adresse);
      if (nb_bits_data==8) Clear_ORG(); else Set_ORG();
      Clear_CLK();
      Clear_DI();
      Clear_CS();
      Crenaux_CLK();
      if (Lire_DO()==1) // Vérifier que la sortie n'est pas à "0" !
      {
        Set_CS();
        Crenaux_CLK();
        for (int posi_bit=(nb_bits_adresse+2);posi_bit>=0;posi_bit--)
        {
          int bit=(code >> posi_bit) & 1;
          Fixer_DI(bit);
          Crenaux_CLK();
        }
        for (int posi_bit=(nb_bits_data-1);posi_bit>=0;posi_bit--)
        {
          int bit=(data >> posi_bit) & 1;
          Fixer_DI(bit);
          Crenaux_CLK();
        }
        Clear_DI();
        Clear_CS();
        Crenaux_CLK();
        resultat=0;
        Set_CS();
        attente1=0; attente2=0;
        while (((Lire_DO()==1)&&(Lire_RDY_BUSY()==1))&&(attente1<30000))
          attente1++;
        while (((Lire_DO()==0)||(Lire_RDY_BUSY()==0))&&(attente2<30000))
          attente2++;
        if ((attente1==30000)||(attente2==30000)) resultat=-4;
      } else resultat=-5;
    } else resultat=-2;
  } else resultat=-1;
  return resultat;
}

//---------------------------------------------------------------------------

// Ecriture d'une donnée 8 ou 16 bits à une adresse précise :
// 'nb_bits_adresse' : nombre de bits d'adresse (6 pour une 9306 ou une 9346).
// 'nb_bits_data' : nombre de bits de donnée (8 ou 16 selon le mode et ORG).
// 'buf' : pointeur sur un tableau d'entiers 'int' contenant les données.
// 'nbdata' : nombre de données à ecrire (64 pour une 9346 en mode 16 bits).
// Renvoie 0 si tout a bien fonctionné, sinon renvoie un nombre négatif qui
// indique la nature de l'erreur (consultez la fonction "MessageErreur").
int EcrireTout(int nb_bits_adresse, int nb_bits_data, int * buf, int nbdata)
{
  int resultat;
  Form1->PB->Position=0;
  Form1->PB->Visible=true;
  for (int adresse=0;adresse<nbdata;adresse++)
  {
    Form1->PB->Position=(int) (100.0*(adresse+1)/nbdata);
    resultat=EcrireAdresse(nb_bits_adresse,adresse,nb_bits_data,buf[adresse]);
    if (resultat!=0) adresse=nbdata;
  }
  Form1->PB->Visible=false;
  return resultat;
}

//---------------------------------------------------------------------------

// Programmation complète de la mémoire avec une valeur unique :
// 'nb_bits_adresse' : nombre de bits d'adresse (6 pour une 9306 ou une 9346).
// 'nb_bits_data' : nombre de bits de donnée (8 ou 16 selon le mode et ORG).
// 'data' : Donnée à écrire dans toute la mémoire.
// Renvoie 0 si tout a bien fonctionné, sinon renvoie un nombre négatif qui
// indique la nature de l'erreur (consultez la fonction "MessageErreur").
int RemplirAvec(int nb_bits_adresse, int nb_bits_data, int data)
{
  int resultat,code,attente1,attente2;

  if (PortPRN!=0)
  {
    if (Teste_Alim_ON())
    {
      code=(0x00000011 << (nb_bits_adresse-2));
      if (nb_bits_data==8) Clear_ORG(); else Set_ORG();
      Clear_CLK();
      Clear_DI();
      Clear_CS();
      Crenaux_CLK();
      if (Lire_DO()==1) // Vérifier que la sortie n'est pas à "0" !
      {
        Set_CS();
        Crenaux_CLK();
        // Commande WRAL :
        for (int posi_bit=(nb_bits_adresse+2);posi_bit>=0;posi_bit--)
        {
          int bit=(code >> posi_bit) & 1;
          Fixer_DI(bit);
          Crenaux_CLK();
        }
        for (int posi_bit=(nb_bits_data-1);posi_bit>=0;posi_bit--)
        {
          int bit=(data >> posi_bit) & 1;
          Fixer_DI(bit);
          Crenaux_CLK();
        }
        Clear_DI();
        Clear_CS();
        Crenaux_CLK();
        resultat=0;
        Set_CS();
        attente1=0; attente2=0;
        while (((Lire_DO()==1)&&(Lire_RDY_BUSY()==1))&&(attente1<30000))
          attente1++;
        while (((Lire_DO()==0)||(Lire_RDY_BUSY()==0))&&(attente2<30000))
          attente2++;
        if ((attente1==30000)||(attente2==30000)) resultat=-4;
      } else resultat=-5;
    } else resultat=-2;
  } else resultat=-1;
  return resultat;
}

//---------------------------------------------------------------------------

// Met à jour les variables internes du programme caractérisant la mémoire.
// Paramètres passés à la routine :
// - 'typemem' : constante T9306 à T5913 définissant le type de la mémoire.
// - 'mode_16_bits' : true ou false selon si l'on est en mode 16 ou 8 bits.
// Paramètres (= variables globales du programme) mis à jour :
// - booléen 'Mode16bits' (indique le mode 8 ou 16 bits, selon broche ORG).
// - entier 'TMtype' : Type (modèle) de mémoire (T9306, T9346, etc).
// - entier 'TMnbd'  : nombre de bits des données, 8 ou 16, selon 'Mode16bits'.
// - entier 'TMnba'  : nombre de bits constituant l'adresse, identique pour les
//                     9306 et 9346, les 9356 et 9366, et les 9376 et 9386.
// - entier 'TMtaille' : nombre d'adresses effectives, selon mode 8 ou 16 bits.
void ParametresMemoire(int typemem, bool mode_16_bits)
{
  Mode16bits=mode_16_bits;
  TMtype=typemem;
  if (Mode16bits)
  {
    TMnbd=16;
    switch(TMtype)
    {
      case T9306 : TMtaille=16;   TMnba=6;  break;
      case T9346 : TMtaille=64;   TMnba=6;  break;
      case T9356 : TMtaille=128;  TMnba=8;  break;
      case T9366 : TMtaille=256;  TMnba=8;  break;
      case T9376 : TMtaille=512;  TMnba=10; break;
      case T9386 : TMtaille=1024; TMnba=10; break;
      case T5911 : TMtaille=64;   TMnba=8;  break;
      case T5922 : TMtaille=128;  TMnba=9;  break;
      case T5913 : TMtaille=256;  TMnba=10; break;
    }
  }
  else
  {
    TMnbd=8;
    switch(TMtype)
    {
      case T9306 : TMtaille=32;   TMnba=7;  break;
      case T9346 : TMtaille=128;  TMnba=7;  break;
      case T9356 : TMtaille=256;  TMnba=9;  break;
      case T9366 : TMtaille=512;  TMnba=9;  break;
      case T9376 : TMtaille=1024; TMnba=11; break;
      case T9386 : TMtaille=2048; TMnba=11; break;
      case T5911 : TMtaille=128;  TMnba=9;  break;
      case T5922 : TMtaille=256;  TMnba=10; break;
      case T5913 : TMtaille=512;  TMnba=11; break;
    }
  }
}

//---------------------------------------------------------------------------

// Routine de gestion de l'appui sur le bouton [Quitter] :
// lancer fermeture du programme.
void __fastcall TForm1::BoutonQuitterClick(TObject *Sender)
{
  Close();
}

//---------------------------------------------------------------------------

// Routine de gestion de l'appui sur le bouton [Allumer l'alimentation] :
void __fastcall TForm1::BoutonAlimONClick(TObject *Sender)
{
  if (Alim_ON()) Etat->Caption="Mémoire Alimentée.";
            else MessageErreur(-1);
}

//---------------------------------------------------------------------------

// Routine de gestion de l'appui sur le bouton [Eteindre l'alimentation] :
void __fastcall TForm1::BoutonAlimOFFClick(TObject *Sender)
{
  if (Alim_OFF()) Etat->Caption="Mémoire éteinte.";
             else MessageErreur(-1);
}

//---------------------------------------------------------------------------

// Routine lancée automatiquement au démarrage du programme, avant
// l'affichage ; c'est ici que sont initialisées toutes les variables :
void __fastcall TForm1::FormCreate(TObject *Sender)
{
  HMENU       hMenu;
  int         valeur,Xfen,Yfen;
  TRegistry   *reg = new TRegistry();

  // Modifier le menu système :
  hMenu=GetSystemMenu(Handle,false);
  RemoveMenu(hMenu,4,MF_BYPOSITION);
  RemoveMenu(hMenu,2,MF_BYPOSITION);
  RemoveMenu(hMenu,0,MF_BYPOSITION);
  InsertMenu(hMenu,2,MF_BYPOSITION+MF_SEPARATOR,0,NULL);
  InsertMenu(hMenu,3,MF_BYPOSITION+MF_STRING,3001,"&Schéma électrique");
  InsertMenu(hMenu,4,MF_BYPOSITION+MF_STRING,3002,"A &propos...");
  InsertMenu(hMenu,5,MF_BYPOSITION+MF_STRING,3003,
                      "P&urger le registre et quitter");

  // Initialisation des variables :
  sauverConfig=true;
  Application->Title="MW Prog";
  Xfen=(Screen->Width-Width)/2;
  Yfen=(Screen->Height-Height)/2;
  randomize();
  TMtaille=64;
  TMtype=T9346;
  TMnba=6;
  TMnbd=16;
  PB->Width=616;
  RGmode->ItemIndex=1;
  CBtypeF->ItemIndex=0;
  Mode16bits=true;
  pointeur=0;
  PortPRN=0;
  Tempo=1;

  // Initialisation du buffer et de son aspect :
  for (int k=0;k<2048;k++) buffer[k]=0xFFFF;
  SGB->Col=1; SGB->Row=1; // Case active du buffer
  SGB->Cells[0][0]=" $000";
  for (int k=0;k<8;k++)
    SGB->Cells[k+1][0]=" $"+IntToHex(k,1)+"/"+IntToHex(k+8,1);
  for (int k=0;k<128;k++)
    SGB->Cells[0][k+1]=" $"+IntToHex(k << 3,3);
  CBtype->ItemIndex=TMtype;

  // Chargement des paramètres sauvegardés dans la base de registre :
  reg->RootKey=HKEY_CURRENT_USER;
  reg->LazyWrite=true;
  if (reg->OpenKey("Software\\COL2000\\MWprog",false))
  {
    if (reg->ValueExists("X_origine"))
    {
      valeur=reg->ReadInteger("X_origine");
      if (valeur<-200) valeur=0;
      if (valeur>(Screen->Width-40)) valeur=Screen->Width-40;
      Xfen=valeur;
    }

    if (reg->ValueExists("Y_origine"))
    {
      valeur=reg->ReadInteger("Y_origine");
      if (valeur<-20) valeur=0;
      if (valeur>(Screen->Height-40)) valeur=Screen->Height-40;
      Yfen=valeur;
    }

    if (reg->ValueExists("Mode_8_16_bits"))
    {
      valeur=reg->ReadInteger("Mode_8_16_bits");
      if ((valeur==0)||(valeur==1)) RGmode->ItemIndex=valeur;
    }

    if (reg->ValueExists("Type_Memoire"))
    {
      valeur=reg->ReadInteger("Type_Memoire");
      if ((valeur>=T9306)&&(valeur<=T5913)) CBtype->ItemIndex=valeur;
    }

    if (reg->ValueExists("Type_Fichier"))
    {
      valeur=reg->ReadInteger("Type_Fichier");
      if ((valeur>=T9306)&&(valeur<=T5913)) CBtypeF->ItemIndex=valeur;
    }

    if (reg->ValueExists("Nom_Fichier"))
    {
      AnsiString nom=reg->ReadString("Nom_Fichier");
      ODouvrir->FileName=nom;
      SDsauver->FileName=nom;
    }

    if (reg->ValueExists("Port_Actif"))
    {
      valeur=reg->ReadInteger("Port_Actif");
      if ((valeur>=0)&&(valeur<=2))
      {
        RGport->ItemIndex=valeur;
        RGportClick(Form1);
      }
      if (reg->ValueExists("Alimentation_ON"))
      {
        if (reg->ReadBool("Alimentation_ON")) Alim_ON();
      }
    }

    reg->CloseKey();
  }
  delete reg;

  // Mise à jour du buffer + tenir compte du mode 8/16 bits & type de mémoire :
  CBtypeChange(Form1);

  // Positionner la fenêtre :
  Form1->Left=Xfen;
  Form1->Top=Yfen;
}

//---------------------------------------------------------------------------

// Routine lancée automatiquement àl'arrêt du programme :
void __fastcall TForm1::FormClose(TObject *Sender, TCloseAction &Action)
{
  TRegistry *reg = new TRegistry();

  reg->RootKey=HKEY_CURRENT_USER;
  reg->LazyWrite=false;
  if (sauverConfig)
  { // Sauvegarder les paramètres dans la base de registre :
    reg->OpenKey("Software\\COL2000\\MWprog",true);
    reg->WriteInteger("X_origine",Form1->Left);
    reg->WriteInteger("Y_origine",Form1->Top);
    reg->WriteInteger("Mode_8_16_bits",RGmode->ItemIndex);
    reg->WriteInteger("Type_Memoire",CBtype->ItemIndex);
    reg->WriteInteger("Type_Fichier",CBtypeF->ItemIndex);
    reg->WriteString ("Nom_Fichier",SDsauver->FileName);
    reg->WriteInteger("Port_Actif",RGport->ItemIndex);
    reg->WriteBool   ("Alimentation_ON",Teste_Alim_ON());
    reg->CloseKey();
  }
  else
  { // Ou bien supprimer les clefs créées dans la base :
    reg->DeleteKey("Software\\COL2000\\MWprog");
    MessageBeep(0xFFFFFFFF);
  }
  delete reg;
  // Ré-initialiser le port, s'il est ouvert (la mémoire est alors éteinte) :
  if (PortPRN!=0) InitPort(PortPRN);
}

//---------------------------------------------------------------------------

// D'après le livre de Gérard LEBLANC "Borland C++ Builder 3"
// aux editions "Eyrolles" :
// Astuce permettant d'exécuter les commandes rajoutées dans
// le menu système au début de la routine "FormCreate" ; ne
// pas oublier de définir la routine dans le fichier d'entête
// "mwprogu1.h" :
MESSAGE void __fastcall TForm1::WMSysCommand(TMessage &Message)
{
  switch(Message.WParam)
  {
    case 3001 : // Commande "Schéma électrique" :
                // Choisir l'onglet du schéma dans la fiche Form3 :
                Form3->PCsv->ActivePage=Form3->TS1;
                // Et afficher la fiche n°3 :
                Form3->ShowModal();
                break;
    case 3002 : // Commande "A propos..." :
                // Choisir l'onglet "Version" dans la fiche Form3 :
                Form3->PCsv->ActivePage=Form3->TS2;
                // Et afficher la fiche n°3 :
                Form3->ShowModal();
                break;
    case 3003 : // Commande "Purger le registre et quitter..." :
                if (IDOK==
        Application->MessageBox(
        "Clef  [HKEY_CURRENT_USER\\Software\\COL2000\\MWprog]\n\n"
        "Le programme sauvegarde ses paramètres dans la base de "
        "registre de Windows ; si vous décidez de ne plus l'utiliser, "
        "cette option vous permet de le quitter en retirant toutes "
        "les informations qui ont été placées dans la base. En cas "
        "d'utilisation ultérieure, le programme utilisera simplement "
        "ses paramètres par défaut, comme lors du premier lancement.\n\n"
        "Voulez-vous quitter le programme en purgeant la base de registre ?",
        " Purger le registre et quitter...",
        MB_ICONWARNING|MB_OKCANCEL))
        { sauverConfig=false; Close(); }
                break;
  }
  TForm::Dispatch(&Message);
}

//---------------------------------------------------------------------------

// Routine associée au bouton [Lire toute la mémoire] :
void __fastcall TForm1::BoutonLireToutClick(TObject *Sender)
{
  Etat->Caption=" ";
  SGB->Color=clAqua;
  SGB->Repaint();
  switch(LireTout(TMtaille,TMnba,TMnbd))
  {
    case 0  : Sleep(100);
              AfficheBuffer();
              Etat->Caption="Lecture terminée.";
              break;
    case -1 : MessageErreur(-1); break;
    case -2 : MessageErreur(-2); break;
    case -3 : MessageErreur(-3); break;
    case -5 : MessageErreur(-5); break;
  }
  SGB->Color=clWindow;
}

//---------------------------------------------------------------------------

// Lit et affiche l'état des sorties "DO" et "RDY/Busy" de la mémoire
// ("RDY/Busy" = broche 7, n'a de sens que pour les 59Cxx) :
void __fastcall TForm1::BoutonLireOutClick(TObject *Sender)
{
  if (PortPRN!=0) Etat->Caption="Sortie DO à "+IntToStr(Lire_DO())+"   ;   "+
                                "sortie Busy à "+IntToStr(Lire_RDY_BUSY());
             else MessageErreur(-1);
}

//---------------------------------------------------------------------------

// Modifie le choix du port parallèle actif :
void __fastcall TForm1::RGportClick(TObject *Sender)
{
  int ancien_port,nouveau_port;

  switch(PortPRN)
  {
    case LPT1 : ancien_port=0; break;
    case LPT2 : ancien_port=1; break;
    case LPT3 : ancien_port=2; break;
    default   : ancien_port=3;
  }
  nouveau_port=RGport->ItemIndex;
  if (ancien_port!=nouveau_port)
  {
    if (ancien_port!=3)
    {
      InitPort(PortPRN);
    }
    switch(nouveau_port)
    {
      case 0   : InitPort(LPT1); break;
      case 1   : InitPort(LPT2); break;
      case 2   : InitPort(LPT3); break;
      default  : PortPRN=0;
    }

    // Avec "InitPort", PortPRN est mis à jour uniquement
    // s'il existe physiquement un port à cette adresse,
    // sinon PortPRN prend la valeur "0" :
    if (PortPRN==0)
    {
      STport->Color=clPurple;
      STport->Font->Color=clRed;
      STport->Caption="Port éteint.";
      if (nouveau_port<3)
      {
        // LPT1, 2 ou 3 pointé, mais inexistant :
        Etat->Caption="Le port parallèle LPT"+IntToStr(nouveau_port+1)+
                      " est indisponible !";
        RGport->ItemIndex=3;
      }    // Aucun port pointé :
      else Etat->Caption="Port désactivé.";
    }
    else
    {
      STport->Color=clGreen;
      STport->Font->Color=clLime;
      STport->Caption="LPT"+IntToStr(nouveau_port+1)+" actif";
      Etat->Caption="Le port parallèle LPT"+IntToStr(nouveau_port+1)+
                    " a été activé.";
    }
  }
}

//---------------------------------------------------------------------------

// Routine associée au bouton [CS à 0] pour
// forcer l'entrée CS de la mémoire à "0":
void __fastcall TForm1::BoutonCS0Click(TObject *Sender)
{
  if (Clear_CS()) Etat->Caption="CS mis à 0.";
             else MessageErreur(-1);
}

//---------------------------------------------------------------------------

// Routine associée au bouton [CS à 1] pour
// forcer l'entrée CS de la mémoire à "1":
void __fastcall TForm1::BoutonCS1Click(TObject *Sender)
{
  if (Set_CS()) Etat->Caption="CS mis à 1.";
           else MessageErreur(-1);
}

//---------------------------------------------------------------------------

// Routine associée au bouton [CLK à 0] pour
// forcer l'entrée CLK de la mémoire à "0":
void __fastcall TForm1::BoutonCLK0Click(TObject *Sender)
{
  if (Clear_CLK()) Etat->Caption="CLK mis à 0.";
              else MessageErreur(-1);
}

//---------------------------------------------------------------------------

// Routine associée au bouton [CLK à 1] pour
// forcer l'entrée CLK de la mémoire à "1":
void __fastcall TForm1::BoutonCLK1Click(TObject *Sender)
{
  if (Set_CLK()) Etat->Caption="CLK mis à 1.";
            else MessageErreur(-1);
}

//---------------------------------------------------------------------------

// Routine associée au bouton [DI à 0] pour
// forcer l'entrée DI de la mémoire à "0":
void __fastcall TForm1::BoutonDI0Click(TObject *Sender)
{
  if (Clear_DI()) Etat->Caption="DI mis à 0.";
             else MessageErreur(-1);
}

//---------------------------------------------------------------------------

// Routine associée au bouton [DI à 1] pour
// forcer l'entrée DI de la mémoire à "1":
void __fastcall TForm1::BoutonDI1Click(TObject *Sender)
{
  if (Set_DI()) Etat->Caption="DI mis à 1.";
           else MessageErreur(-1);
}

//---------------------------------------------------------------------------

// Routine associée au bouton [ORG à 0] pour
// forcer l'entrée ORG de la mémoire à "0":
void __fastcall TForm1::BoutonORG0Click(TObject *Sender)
{
  if (Clear_ORG()) Etat->Caption="ORG mis à 0.";
             else MessageErreur(-1);
}

//---------------------------------------------------------------------------

// Routine associée au bouton [ORG à 1] pour
// forcer l'entrée ORG de la mémoire à "1":
void __fastcall TForm1::BoutonORG1Click(TObject *Sender)
{
  if (Set_ORG()) Etat->Caption="ORG mis à 1.";
           else MessageErreur(-1);
}

//---------------------------------------------------------------------------

// Routine associée au bouton [Effacer toute la mémoire] ;
// elle envoie la commande ERAL (Erase All = tout effacer) :
void __fastcall TForm1::BoutonERALClick(TObject *Sender)
{
  switch(Commande(ERAL,TMnba,0,Mode16bits))
  {
    case  0 : Etat->Caption="Effacement de la mémoire effectué."; break;
    case -1 : MessageErreur(-1); break;
    case -2 : MessageErreur(-2); break;
    case -4 : MessageErreur(-4); break;
    case -5 : MessageErreur(-5); break;
  }
}

//---------------------------------------------------------------------------

// Routine associée au bouton [Déverrouiller les données] ; elle envoie
// la commande EWEN (Eeprom Write Enable = autoriser écriture mémoire) :
void __fastcall TForm1::BoutonEWENClick(TObject *Sender)
{
  switch(Commande(EWEN,TMnba,0,Mode16bits))
  {
    case  0 : Etat->Caption="Les données ont été déverrouillées."; break;
    case -1 : MessageErreur(-1); break;
    case -2 : MessageErreur(-2); break;
    case -5 : MessageErreur(-5); break;
  }
}

//---------------------------------------------------------------------------

// Routine associée au bouton [Verrouiller les données] ; elle envoie
// la commande EWDS (Eeprom Write Disable = invalider écriture mémoire) :
void __fastcall TForm1::BoutonEWDSClick(TObject *Sender)
{
  switch(Commande(EWDS,TMnba,0,Mode16bits))
  {
    case  0 : Etat->Caption="Les données ont été verrouillées."; break;
    case -1 : MessageErreur(-1); break;
    case -2 : MessageErreur(-2); break;
    case -5 : MessageErreur(-5); break;
  }
}

//---------------------------------------------------------------------------

// Routine associée au bouton [Effacer la case mémoire] ;
// elle envoie la commande ERASE (= effacer) :
void __fastcall TForm1::BoutonERASEClick(TObject *Sender)
{
  if (pointeur<TMtaille)
  {
    switch(Commande(ERASE,TMnba,pointeur,Mode16bits))
    {
      case  0 : Etat->Caption="Effacement de la cellule $"+
                IntToHex(pointeur,3)+"effectué."; break;
      case -1 : MessageErreur(-1); break;
      case -2 : MessageErreur(-2); break;
      case -4 : MessageErreur(-4); break;
      case -5 : MessageErreur(-5); break;
    }
  } else MessageErreur(-6);
}

//---------------------------------------------------------------------------

// Lorsque l'on chage la cellule active du tableau (buffer),
// cette routine recalcule l'adresse pointée :
void __fastcall TForm1::SGBSelectCell(TObject *Sender, int Col, int Row,
      bool &CanSelect)
{
  // Se souvenir que la colonne et la ligne 0 (grises) ne contiennent
  // pas les données, mais des infos pour se situer dans l'espace mémoire.
  if (Mode16bits)
  {
    pointeur=Col+8*Row-9;
    SGB->Cells[0][0]=" $"+IntToHex(pointeur,3);
  }
  else
  {
    pointeur=Col+16*Row-17;
    SGB->Cells[0][0]=" Adresse $"+IntToHex(pointeur,3);
  }
}

//---------------------------------------------------------------------------

// Routine associée au bouton [Lire la case mémoire] ; lit
// dans la mémoire la valeur correspondant à l'adresse pointée :
void __fastcall TForm1::BoutonLireCaseClick(TObject *Sender)
{
  if (pointeur<TMtaille)
  {
    int val;

    val=LireAdresse(pointeur,TMnba,TMnbd);
    if (val>=0) { buffer[pointeur]=val; AfficheBuffer(); }
    switch(val)
    {
      case -1 : MessageErreur(-1); break;
      case -2 : MessageErreur(-2); break;
      case -3 : MessageErreur(-3); break;
      case -5 : MessageErreur(-5); break;
      default : Etat->Caption="L'adresse $"+IntToHex(pointeur,3)+
                " contient la valeur $";
                if (Mode16bits) Etat->Caption=Etat->Caption+IntToHex(val,4);
                           else Etat->Caption=Etat->Caption+IntToHex(val,2);
    }
  } else MessageErreur(-6);
}

//---------------------------------------------------------------------------

// Lorsque l'on double-clique sur une case du buffer, le système affiche
// la fiche Form2 qui permet d'éditer la valeur numérique contenue à cette
// adresse (modification du buffer, pas de la mémoire !) :
void __fastcall TForm1::SGBDblClick(TObject *Sender)
{
  if (pointeur<TMtaille) Form2->ShowModal();
}

//---------------------------------------------------------------------------

// (comme précédemment)
// Lorsque l'on appuie sur [Entrée], et que le buffer est l'élément actif,
// le système affiche la fiche Form2 qui permet d'éditer la valeur numérique
// contenue à l'adresse active :
void __fastcall TForm1::SGBKeyPress(TObject *Sender, char &Key)
{
  if (Key==VK_RETURN) { if (pointeur<TMtaille) Form2->ShowModal(); }
}

//---------------------------------------------------------------------------

// Routine associée au bouton [Ecrire la case mémoire] ; écrit
// dans la mémoire la valeur correspondant à l'adresse pointée
// du buffer :
void __fastcall TForm1::BoutonEcrireCaseClick(TObject *Sender)
{
  if (pointeur<TMtaille)
  {
    switch(EcrireAdresse(TMnba,pointeur,TMnbd,buffer[pointeur]))
    {
      case -1 : MessageErreur(-1); break;
      case -2 : MessageErreur(-2); break;
      case -4 : MessageErreur(-4); break;
      case -5 : MessageErreur(-5); break;
      default : if (Mode16bits)
                  Etat->Caption="Valeur $"+IntToHex(buffer[pointeur],4)+
                              " programmée à l'adresse $"+IntToHex(pointeur,3);
                else
                  Etat->Caption="Valeur $"+IntToHex(buffer[pointeur],2)+
                              " programmée à l'adresse $"+IntToHex(pointeur,3);
    }
  } else MessageErreur(-6);
}

//---------------------------------------------------------------------------

// Routine associée au bouton [La case > toute la mémoire] ; programme
// toutes les cases de la mémoire avec la valeur numérique de l'adresse
// pointée du buffer (commande WRAL = Write All = écrire tout) :
void __fastcall TForm1::BoutonRemplirAvecClick(TObject *Sender)
{
  if (pointeur<TMtaille)
  {
    switch(RemplirAvec(TMnba,TMnbd,buffer[pointeur]))
    {
     case -1 : MessageErreur(-1); break;
     case -2 : MessageErreur(-2); break;
     case -4 : MessageErreur(-4); break;
     case -5 : MessageErreur(-5); break;
     default : if (Mode16bits) Etat->Caption="Memoire remplie avec la valeur $"
                                             +IntToHex(buffer[pointeur],4)+".";
                          else Etat->Caption="Memoire remplie avec la valeur $"
                                             +IntToHex(buffer[pointeur],2)+".";
    }
  } else MessageErreur(-6);
}

//---------------------------------------------------------------------------

// Routine associée au bouton [Ecrire toute la mémoire] ; programme
// chaque case de la mémoire avec la valeur numérique contenue dans
// chaque case associée du buffer :
void __fastcall TForm1::BoutonEcrireToutClick(TObject *Sender)
{
  switch(EcrireTout(TMnba,TMnbd,buffer,TMtaille))
  {
    case -1 : MessageErreur(-1); break;
    case -2 : MessageErreur(-2); break;
    case -4 : MessageErreur(-4); break;
    case -5 : MessageErreur(-5); break;
    default : Etat->Caption="Toute la mémoire a été programmée.";
  }
}

//---------------------------------------------------------------------------

// Routine associée au bouton [comparer mémoire/buffer] ; relit chaque
// adresse de la mémoire et compare son contenu avec la valeur numérique
// de l'adresse correspondante du buffer :
void __fastcall TForm1::BoutonComparerClick(TObject *Sender)
{
  int resultat=Comparer(TMtaille,TMnba,TMnbd);
  switch(resultat)
  {
    case -1 : MessageErreur(-1); break;
    case -2 : MessageErreur(-2); break;
    case -3 : MessageErreur(-3); break;
    case -5 : MessageErreur(-5); break;
    case 0  : Etat->Caption="Comparaison terminée : aucune différence."; break;
    case 1  : Etat->Caption="Comparaison terminée : il y a une erreur !"; break;
    default : Etat->Caption="Comparaison terminée : il y a "+
              IntToStr(resultat)+" erreurs !";
  }
}

//---------------------------------------------------------------------------

// Routine associée aux boutons [Schéma] et [Version] ; le système affiche
// la fiche Form3 et l'onglet correspondant soit au Schéma, soit aux
// informations de version du programme ("A propos...") :
void __fastcall TForm1::BoutonSchemaVersionClick(TObject *Sender)
{
  if (Sender==BoutonSchema)  Form3->PCsv->ActivePage=Form3->TS1;
  if (Sender==BoutonVersion) Form3->PCsv->ActivePage=Form3->TS2;
  Form3->ShowModal();
}

//---------------------------------------------------------------------------

// Routine associée au bouton [00] ; le buffer est rempli avec la
// valeur numérique 00 (mode 8 bits) ou 0000 (mode 16 bits) ; la
// totalité du buffer est concernée, pas seulement la zone affichée.
void __fastcall TForm1::BoutonBuffer00Click(TObject *Sender)
{
  SGB->Color=clGreen;
  SGB->Repaint();
  Sleep(100);
  if (Mode16bits) for (int n=0;n<1024;n++) buffer[n]=0x0000;
             else for (int n=0;n<2048;n++) buffer[n]=0x00;
  AfficheBuffer();
  SGB->Color=clWindow;
}

//---------------------------------------------------------------------------

// Routine associée au bouton [FF] ; le buffer est rempli avec la
// valeur numérique FF (mode 8 bits) ou FFFF (mode 16 bits) ; la
// totalité du buffer est concernée, pas seulement la zone affichée.
void __fastcall TForm1::BoutonBufferFFClick(TObject *Sender)
{
  SGB->Color=clGreen;
  SGB->Repaint();
  Sleep(100);
  if (Mode16bits) for (int n=0;n<1024;n++) buffer[n]=0xFFFF;
             else for (int n=0;n<2048;n++) buffer[n]=0xFF;
  AfficheBuffer();
  SGB->Color=clWindow;
}

//---------------------------------------------------------------------------

// Routine associée au bouton [cloner] ; le buffer est rempli avec la
// valeur numérique présente dans la case active (case bleue du buffer) ;
// la totalité du buffer est concernée, pas seulement la zone affichée.
void __fastcall TForm1::BoutonBufferClonerClick(TObject *Sender)
{
  if (pointeur<TMtaille)
  {
    int val;

    SGB->Color=clGreen;
    SGB->Repaint();
    Sleep(100);
    val=buffer[pointeur];
    if (Mode16bits) for (int n=0;n<1024;n++) buffer[n]=val;
               else for (int n=0;n<2048;n++) buffer[n]=val;
    AfficheBuffer();
    SGB->Color=clWindow;
  } else MessageErreur(-6);
}

//---------------------------------------------------------------------------

// Routine associée au bouton [Aléatoire] ; chaque case du buffer est
// rempli avec une valeur numérique aléatoire ; la totalité du buffer
// est concernée, pas seulement la zone affichée.
void __fastcall TForm1::BoutonBufferRandomClick(TObject *Sender)
{
  SGB->Color=clGreen;
  SGB->Repaint();
  Sleep(100);
  if (Mode16bits) for (int n=0;n<1024;n++) buffer[n]=random(65536);
             else for (int n=0;n<2048;n++) buffer[n]=random(256);
  AfficheBuffer();
  SGB->Color=clWindow;
}

//---------------------------------------------------------------------------

// Routine associée au bouton [MSB<->LSB] ; en mode 16 bits, les octets
// de poids fort et de poids faible sont échangés ; en mode 8 bits, les
// octets pairs et impairs sont échangés.
// La totalité du buffer est concernée, et pas uniquement la zone affichée.
void __fastcall TForm1::BoutonBufferMSBLSBClick(TObject *Sender)
{
  unsigned int data,data1,data2;

  SGB->Color=clGreen;
  SGB->Repaint();
  Sleep(100);
  if (Mode16bits)
  {
    for (int n=0;n<1024;n++)
    {
      data=(unsigned int) buffer[n];
      data1=(data & 0x0000FF00) >> 8;
      data2=(data & 0x000000FF) << 8;
      data=(data & 0xFFFF0000) | data1 | data2;
      buffer[n]=(int) data;
    }
  }
  else
  {
    for (int n=0;n<1024;n++)
    {
      data=buffer[2*n];
      buffer[2*n]=buffer[2*n+1];
      buffer[2*n+1]=data;
    }
  }
  AfficheBuffer();
  SGB->Color=clWindow;
}

//---------------------------------------------------------------------------

// Routine associée au bouton [Complémenter] ; chaque case du buffer est
// complémentée : $0000 devient $FFFF, $0001 devient $FFFE, etc.
// La totalité du buffer est concernée, pas seulement la zone affichée.
void __fastcall TForm1::BoutonBufferComplementClick(TObject *Sender)
{
  SGB->Color=clGreen;
  SGB->Repaint();
  Sleep(100);
  if (Mode16bits) for (int n=0;n<1024;n++) buffer[n]^=0x0000FFFF;
             else for (int n=0;n<2048;n++) buffer[n]^=0x000000FF;
  AfficheBuffer();
  SGB->Color=clWindow;
}

//---------------------------------------------------------------------------

// Routine de gestion du changement de type de mémoire :
void __fastcall TForm1::CBtypeChange(TObject *Sender)
{
  // Mettre à jour les paramètres (nombre d'adresse, de bits
  // de contrôle, ...) propres à ce modèle de mémoire dans
  // les variables globales (voir fonction "ParametresMemoire") :
  ParametresMemoire(CBtype->ItemIndex,Mode16bits);

  // Désactiver le bouton [Effacer la case mémoire] pour les 59Cxx uniquement
  // car l'instruction correspondante au "ERASE" des 93x6 n'existe pas :
  BoutonERASE->Enabled=(TMtype<T5911);

  // Mettre à jour l'affichage du buffer (concrètement, il faut n'afficher
  // que le nombre de cases correspondant à la taille de la mémoire).
  AfficheBuffer();

  // Réactualiser l'indication de taille de la mémoire selon le format :
  RGmode->Caption=" "+IntToStr(TMtaille)+" mots de : ";
}

//---------------------------------------------------------------------------

// Routine prenant en charge le changement de mode 8 / 16 bits,
// cela consiste essentiellement à redessiner le buffer et à
// actualiser son organisation et son contenu :
void __fastcall TForm1::RGmodeClick(TObject *Sender)
{
  // On vérifie la différence entre le mode en cours et le mode demandé :
  if (Mode16bits!=(RGmode->ItemIndex==1))
  {
    // On valide le nouveau mode :
    ParametresMemoire(TMtype,(RGmode->ItemIndex==1));
    if (Mode16bits)
    {
      // Passage du mode 8 bits au mode 16 bits :

      // Réorganiser le contenu :
      for (int n=0;n<1024;n++) buffer[n]=(buffer[2*n]<<8)+buffer[2*n+1];

      // Elargir les colonnes :
      SGB->DefaultColWidth=56;

      // Mettre à jour la case active :
      SGB->Col=((SGB->Col-1)>>1)+1;

      // Réduire le nombre de colonne de (16+1) à (8+1) :
      SGB->ColCount=9;

      // Renuméroter les adresses...
      for (int k=0;k<8;k++)   // Horizontales :
        SGB->Cells[k+1][0]=" $"+IntToHex(k,1)+"/"+IntToHex(k+8,1);
      for (int k=0;k<128;k++) // Verticales :
        SGB->Cells[0][k+1]=" $"+IntToHex(k << 3,3);

      // Recalculer l'adresse pointée par la case active et l'afficher :
      pointeur=SGB->Col+8*SGB->Row-9;
      SGB->Cells[0][0]=" $"+IntToHex(pointeur,3);

      // Mettre à "1" la broche ORG de la mémoire (mode 16 bits) :
      Set_ORG();
    }
    else
    {
      // Passage du mode 16 bits au mode 8 bits :

      // Réorganiser le contenu :
      for (int n=1023;n>=0;n--)
      {
        int val=buffer[n];
        buffer[2*n]=(val & 0x0000FF00)>>8;
        buffer[2*n+1]=(val & 0x000000FF);
      }

      // Réduire la largeur des colonnes, et faire passer
      // leur nombre de (8+1) à (16+1) :
      SGB->DefaultColWidth=23;
      SGB->ColCount=17;

      // Mettre à jour la case active :
      SGB->Col=((SGB->Col-1)<<1)+1;

      // La première colonne (adresses) est plus large que les autres :
      SGB->ColWidths[0]=128;

      // Renuméroter les adresses...
      for (int k=0;k<16;k++)   // Horizontales :
        SGB->Cells[k+1][0]="$"+IntToHex(k,1);
      for (int k=0;k<128;k++)  // Verticales :
        SGB->Cells[0][k+1]=" $"+IntToHex(k << 4,3)+
                           " à $"+IntToHex((k<<4)+15,3)+":";

      // Recalculer l'adresse pointée par la case active et l'afficher :
      pointeur=SGB->Col+16*SGB->Row-17;
      SGB->Cells[0][0]=" Adresse $"+IntToHex(pointeur,3);

      // Mettre à "0" la broche ORG de la mémoire (mode 8 bits) :
      Clear_ORG();
    }

    // Réactualiser le contenu du buffer sous sa nouvelle forme :
    AfficheBuffer();

    // Réactualiser l'indication de taille de la mémoire selon le format :
    RGmode->Caption=" "+IntToStr(TMtaille)+" mots de : ";
  }
}

//---------------------------------------------------------------------------

// Effacement de la barre d'état (texte en jaune sur
// fond bleu) lorsque l'on fait un double-clique :
void __fastcall TForm1::EtatDblClick(TObject *Sender)
{
  Etat->Caption=" ";
}

//---------------------------------------------------------------------------

// Routine d'enregistrement du contenu du buffer dans un fichier
// (la taille dépend de la mémoire sélectionnée) :
void __fastcall TForm1::BoutonEnregistrerFichierClick(TObject *Sender)
{
  AnsiString  Nom;
  int         taille;

  // Tronquer si elle existe l'extension du nom du fichier :
  Nom=SDsauver->FileName.UpperCase();
  taille=Nom.Length();
  if (taille>4)
  {
    if ((Nom.SubString(taille-3,4)==".BIN") ||
        (Nom.SubString(taille-3,4)==".TXT"))
    Nom=SDsauver->FileName.SubString(1,taille-4);
    else Nom=SDsauver->FileName;
  } else Nom=SDsauver->FileName;

  // Et coller la bonne extension selon le type choisi (BIN, ou TXT hexa)
  if (CBtypeF->ItemIndex==0)
  {
    SDsauver->FileName=Nom+".BIN";
    SDsauver->DefaultExt=".BIN";
    SDsauver->Filter="Fichiers binaires (*.BIN) |*.BIN";
  }
  else
  {
    SDsauver->FileName=Nom+".TXT";
    SDsauver->DefaultExt=".TXT";
    SDsauver->Filter="Fichiers textes hexa (*.TXT) |*.TXT";
  }

  // Validation du nom par défaut, ou modification de celui-ci :
  if (SDsauver->Execute())
  {
    // En mode binaire BIN :
    if (CBtypeF->ItemIndex==0)
    {
      FILE *fichier;

      // Création ou écrasement du fichier en mode binaire :
      fichier=fopen(SDsauver->FileName.c_str(),"wb");
      if (fichier!=NULL)
      {
        if (Mode16bits)
        {
          // En mode 16 bits, deux octets par case :
          for (int n=0;n<TMtaille;n++)
            fprintf(fichier,"%c%c",
                    (unsigned char) ((buffer[n] & 0x0000FF00)>>8),
                    (unsigned char)  (buffer[n] & 0x000000FF));
        }
        else
        {
          // En mode 8 bits, un octet par case :
          for (int n=0;n<TMtaille;n++)
            fprintf(fichier,"%c",(unsigned char) (buffer[n] & 0x000000FF));
        }

        // ...et on referme le fichier :
        fclose(fichier);
      }
      else Etat->Caption="Enregistrement du fichier .BIN impossible !";
    }
    else
    {
      // En mode texte TXT Hexa :
      int Fichier;

      Fichier=FileCreate(SDsauver->FileName);
      if (Fichier!=-1)
      {
        if (Mode16bits)
        {
          // En mode 16 bits, on enregistre des paquets de
          // 4 caractères pour chaque mot de 16 bits :
          for (int n=0;n<TMtaille;n++)
          {
            AnsiString val=IntToHex(buffer[n] & 0x0000FFFF,4)+" ";
            if ((n % 8)==7) val=val+"\r\n";
            FileWrite(Fichier,val.c_str(),val.Length());
          }
        }
        else
        {
          // En mode 8 bits, on enregistre des paquets de
          // 2 caractères pour chaque mot de 8 bits :
          for (int n=0;n<TMtaille;n++)
          {
            AnsiString val=IntToHex(buffer[n] & 0x000000FF,2)+" ";
            if ((n % 16)==15) val=val+"\r\n";
            FileWrite(Fichier,val.c_str(),val.Length());
          }
        }

        // ...et on referme le fichier :
        FileClose(Fichier);
      }
      else Etat->Caption="Enregistrement du fichier .TXT impossible !";
    }

    // Indiquer le bon enregistrement, et le nom du fichier créé :
    Etat->Caption="Fichier \""+SDsauver->FileName+"\" enregistré.";

    // Donner à la boîte d'ouverture de fichier le même
    // nom de fichier que celui qui a été créé :
    ODouvrir->FileName=SDsauver->FileName;
  }
  else Etat->Caption="Enregistrement du buffer annulé !";
}

//---------------------------------------------------------------------------

// Routine de gestion d'ouverture d'un fichier, et de chargement de son
// contenu dans le buffer :
void __fastcall TForm1::BoutonOuvrirFichierClick(TObject *Sender)
{
  AnsiString  Nom;
  int         taille;

  // Tronquer si elle existe l'extension du nom du fichier :
  Nom=ODouvrir->FileName.UpperCase();
  taille=Nom.Length();
  if (taille>4)
  {
    if ((Nom.SubString(taille-3,4)==".BIN") ||
        (Nom.SubString(taille-3,4)==".TXT"))
    Nom=ODouvrir->FileName.SubString(1,taille-4);
    else Nom=ODouvrir->FileName;
  } else Nom=ODouvrir->FileName;

  // Et coller la bonne extension selon le type choisi (BIN, ou TXT hexa)
  if (CBtypeF->ItemIndex==0)
  {
    ODouvrir->FileName=Nom+".BIN";
    ODouvrir->DefaultExt=".BIN";
    ODouvrir->Filter="Fichiers binaires (*.BIN) |*.BIN";
  }
  else
  {
    ODouvrir->FileName=Nom+".TXT";
    ODouvrir->DefaultExt=".TXT";
    ODouvrir->Filter="Fichiers textes hexa (*.TXT) |*.TXT";
  }

  // Choisir le fichier :
  if (ODouvrir->Execute())
  {
    // Si c'est un fichier BIN au format binaire :
    if (CBtypeF->ItemIndex==0)
    {
      FILE           *fichier;
      int            nblus,nbvalid;
      unsigned char  *TamponLec;

      // Ouvrir le fichier :
      fichier=fopen(ODouvrir->FileName.c_str(),"rb");

      // Créer une mémoire tampon provisoire :
      TamponLec=(unsigned char *)malloc(2050);

      if ((fichier!=NULL)&&(TamponLec!=NULL))
      {
        // Si tout c'est bien passé, lire le fichier :
        nblus=fread((void *)TamponLec,1,2049,fichier);
        if (nblus>2048) nbvalid=2048; else nbvalid=nblus;

        if (Mode16bits)
        { // En mode 16 bits :
          // Charger des mots de 16 bits dans le buffer
          for (int n=0;n<(nbvalid/2);n++)
          {
            buffer[n]=(int) TamponLec[2*n];
            buffer[n]=(buffer[n]<<8)+TamponLec[2*n+1];
          }
          // Si nombre impair d'octets lus, penser à charger aussi le dernier :
          if ((nbvalid & 1)!=0)
          {
            buffer[nbvalid/2]=(buffer[nbvalid/2] & 0x000000FF)+
                              (TamponLec[nbvalid-1]<<8);
          }
        } // En mode 8 bits :
        else for (int n=0;n<nbvalid;n++) buffer[n]=(int) TamponLec[n];

        // Purger le tampon provisoire et fermer le fichier :
        free(TamponLec);
        fclose(fichier);

        // Indiquer l'ouverture correcte, le nom du fichier ouvert,
        // le nombre d'octets lus :
        Etat->Caption="\""+ODouvrir->FileName+"\" ouvert => ";
        if (nbvalid>1) Etat->Caption=Etat->Caption+
                       IntToStr(nbvalid)+" octets lus.";
                  else Etat->Caption=Etat->Caption+
                       IntToStr(nbvalid)+" octet lu.";

        // Mettre à jour l'affichage :
        AfficheBuffer();

      } else Etat->Caption="Ouverture du fichier .BIN impossible !";
    }
    else
    {
      // Si c'est un fichier texte TXT au format hexa :

      FILE           *fichier;
      int            nblus,nbvalid,valeur;
      unsigned char  *TamponLec,carac;

      // Ouvrir le fichier :
      fichier=fopen(ODouvrir->FileName.c_str(),"rb");

      // Créer une mémoire tampon provisoire :
      TamponLec=(unsigned char *)malloc(4097);

      if ((fichier!=NULL)&&(TamponLec!=NULL))
      {
        // Si tout c'est bien passé, lire le fichier caractère par caractère
        // en ignorant les caractères différents de 0 à 9, A à F, "a" à "f",
        // et remplir le tampon provisoire (4 bits par adresse) :
        nbvalid=0;
        do
        {
          nblus=fread(&carac,1,1,fichier);
          if (nblus==1)
          {
            switch(carac)
            {
              case 'a' :
              case 'b' :
              case 'c' :
              case 'd' :
              case 'e' :
              case 'f' : TamponLec[nbvalid++]=(unsigned char) (carac-'a'+10);
                         break;
              case 'A' :
              case 'B' :
              case 'C' :
              case 'D' :
              case 'E' :
              case 'F' : TamponLec[nbvalid++]=(unsigned char) (carac-'A'+10);
                         break;
              case '0' :
              case '1' :
              case '2' :
              case '3' :
              case '4' :
              case '5' :
              case '6' :
              case '7' :
              case '8' :
              case '9' : TamponLec[nbvalid++]=(unsigned char) (carac-'0');
                         break;

            }
          }
        }
        while ((nblus==1)&&(nbvalid<=4095));

        if (Mode16bits)
        {
          // En mode 16 bits :
          if (nbvalid>0)
          for (int n=0;n<nbvalid;n++)
          {
            // Ajouter au buffer les paquets de 4 bits correspondant à chaque
            // caractère '1'...'9' ou 'A'...'F' rencontré dans le fichier :
            valeur=buffer[n/4];
            switch(n % 4)
            {
              case 0 : valeur&=0x00000FFF;
                       buffer[n/4]=valeur + (((int)TamponLec[n])<<12);
                       break;
              case 1 : valeur&=0x0000F0FF;
                       buffer[n/4]=valeur + (((int)TamponLec[n])<<8);
                       break;
              case 2 : valeur&=0x0000FF0F;
                       buffer[n/4]=valeur + (((int)TamponLec[n])<<4);
                       break;
              case 3 : valeur&=0x0000FFF0;
                       buffer[n/4]=valeur + ((int)TamponLec[n]);
                       break;
            }
          }
        }
        else
        {
          // En mode 8 bits :
          if (nbvalid>0)
          for (int n=0;n<nbvalid;n++)
          {
            // Ajouter au buffer les paquets de 4 bits correspondant à chaque
            // caractère '1'...'9' ou 'A'...'F' rencontré dans le fichier :
            valeur=buffer[n/2];
            switch(n % 2)
            {
              case 0 : valeur&=0x0000000F;
                       buffer[n/2]=valeur + (((int)TamponLec[n])<<4);
                       break;
              case 1 : valeur&=0x000000F0;
                       buffer[n/2]=valeur + ((int)TamponLec[n]);
                       break;
            }
          }
        }

        // Purger le tampon provisoire et fermer le fichier :
        free(TamponLec);
        fclose(fichier);

        // Indiquer l'ouverture correcte, le nom du fichier ouvert,
        // le nombre d'octets lus (au demi-octet près !) :
        Etat->Caption="\""+ODouvrir->FileName+"\" ouvert => ";
        switch(nbvalid)
        {
         case 0  : Etat->Caption=Etat->Caption+"pas d'octet lu.";
                   break;
         case 1  : Etat->Caption=Etat->Caption+"1/2 octet lu.";
                   break;
         case 2  : Etat->Caption=Etat->Caption+"un seul octet lu.";
                   break;
         default : Etat->Caption=Etat->Caption+FloatToStr(nbvalid/2.0)+
                                 " octets lus.";
        }

        // Mettre à jour l'affichage :
        AfficheBuffer();
      }
      else Etat->Caption="Ouverture du fichier .TXT impossible !";

    }

    // Donner à la boîte de sauvegarde de fichier le même
    // nom de fichier que celui qui a été ouvert :
    SDsauver->FileName=ODouvrir->FileName;
  }
  else Etat->Caption="Impossible d'ouvrir le fichier, chargement annulé !";
}

//---------------------------------------------------------------------------



